#include "dmaTest.h"

//demo:使用 USART1做dma的收发，dma理解为一种更加快速传输的机制在外设之间，因为不需经过内存
//串口+dma解决 串口接受发频繁中断的问题（还不知道为什么）

// USART2 PA2 PA3

//引脚复用:单片机的引脚除了基础的GPIO功能外还能作为其他外设的引脚 USART CAN
// 在这边用一个串口去输出PA10的信息

//https://www.cnblogs.com/microxiami/p/3752715.html

void usart1_dma_gpio_init(){
  gpio_init_type gpio_init_struct;
  #if defined (__GNUC__) && !defined (__clang__)
  setvbuf(stdout, NULL, _IONBF, 0); //清空缓存区
   #endif
  // 1. 串口、对应GPIO时钟使能
  crm_periph_clock_enable(CRM_USART1_PERIPH_CLOCK, TRUE);                                    // 1.PA2 USART2 PA9 USART1
  /* enable the gpioa clock */
  crm_periph_clock_enable(CRM_GPIOA_PERIPH_CLOCK, TRUE);                                  //

  /* set default parameter */
  gpio_default_para_init(&gpio_init_struct); //可以看参数

  /* configure the gpio */
  
  // 2. 配置GPIOA3|2
  // PA2-TX PA3-RX（rece） USART2 
  // 我用其他的打印怎么

  //152看配置 
  gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_STRONGER;
  gpio_init_struct.gpio_out_type  = GPIO_OUTPUT_PUSH_PULL; // 推挽输出 手册规定
  gpio_init_struct.gpio_mode = GPIO_MODE_MUX; // 手册 复用输出模式配置
  gpio_init_struct.gpio_pins = GPIO_PINS_9;                                                //4.
  gpio_init_struct.gpio_pull = GPIO_PULL_NONE; //设置上下模式 GPIO无上下拉 这个咋看的？
  gpio_init(GPIOA, &gpio_init_struct); //

  gpio_init_struct.gpio_out_type  = GPIO_OUTPUT_PUSH_PULL; // 推挽输出 手册规定
  gpio_init_struct.gpio_mode = GPIO_MODE_INPUT; // 输入模式
  gpio_init_struct.gpio_pins = GPIO_PINS_10;
  gpio_init_struct.gpio_pull = GPIO_PULL_UP;//上拉模式 手册
  gpio_init(GPIOA, &gpio_init_struct);

  // 3.串口配置
  usart_init(USART1, 115200, USART_DATA_8BITS, USART_STOP_1_BIT);

  // 4.中断配置以及空闲中断使能
  nvic_priority_group_config(NVIC_PRIORITY_GROUP_4);
  nvic_irq_enable(USART1_IRQn, 0, 0); 
  usart_interrupt_enable(USART1, USART_IDLE_INT, TRUE); // 串口总线空闲中断标志位置位 在中断服务函数中判断接收结束 这边可以用到dma+usart空闲

  //串口 DMA收发使能
  // usart_dma_transmitter_enable(USART1,TRUE);
 
  usart_dma_receiver_enable(USART1,TRUE); //接收到tx_buf 内存
  // 用DMA的时候没写
  // usart_parity_selection_config(USART1, USART_PARITY_NONE); //ques感觉这样才能联动DMA中断？ 6 换成事件后直接寄
  
  usart_transmitter_enable(USART1, TRUE);
  usart_receiver_enable(USART1, TRUE);
  // 5.串口使能  
  usart_enable(USART1, TRUE); //使能串口  
}

// uint8_t tx_buff[]="hello world!\r\n"; // 内存 将这块发送出去，所以内存到外设
// 不懂这个tx_buff的意思
// dma 的配置 p122 通道4-tx 5-rx]

// 配置DMA
void at32_dma1_init(){
  dma_init_type dma_init_struct;

  /* 1.enable dma1 clock */
  crm_periph_clock_enable(CRM_DMA1_PERIPH_CLOCK, TRUE);

  /* dma1 channel4 for usart1 tx configuration */
  // 2.先复位再默认配置
  // dma_reset(DMA1_CHANNEL4);
  // dma_default_para_init(&dma_init_struct);
  // // 3.初始化

  // // tx 内存到外设 把buf里面的东西放进usart的tdr，等待tx发出。 buf写进TDR
  // // tx_buf能不能从rx收进来的？ 可以吧，感觉DMA就是一个通道
  // dma_init_struct.buffer_size = COUNTOF(tx_buf)-1; //容量配置                                                                     // 修改
  // dma_init_struct.direction = DMA_DIR_MEMORY_TO_PERIPHERAL; //DMA发送方向 内存到外设
  // dma_init_struct.memory_base_addr = (uint32_t)tx_buf; // 设置内存的地址                                                          //修改
  // dma_init_struct.memory_data_width = DMA_MEMORY_DATA_WIDTH_BYTE; //数据的宽度 1byte 0.5word 1word
  // dma_init_struct.memory_inc_enable = TRUE;//增量模式 prtx_buff会自动++
  // dma_init_struct.peripheral_base_addr = (uint32_t)&USART1->dt; //外设地址USART1,具体看要发到哪个外设上。dt就是数据寄存器，有发送功能   //修改
  // dma_init_struct.peripheral_data_width = DMA_PERIPHERAL_DATA_WIDTH_BYTE;
  // dma_init_struct.peripheral_inc_enable = FALSE; //外设增量不需要，地址不变
  // dma_init_struct.priority = DMA_PRIORITY_MEDIUM; //dma优先级设置
  // dma_init_struct.loop_mode_enable = FALSE; //循环模式，
  // dma_init(DMA1_CHANNEL4, &dma_init_struct);                         // 通道查手册

  /* dma1 channel5 for usart1 rx configuration */
  dma_reset(DMA1_CHANNEL5);
  dma_default_para_init(&dma_init_struct);

//   rx 外设到内存 串口RDR寄存器写入某数据buf
// 内存-》外设（增）
// 外设-》内存（增） 外设总线接收

// L5->RX
  dma_init_struct.buffer_size =  COUNTOF(tx_buf)-1; // 99,每次只有init进一次
  dma_init_struct.direction = DMA_DIR_PERIPHERAL_TO_MEMORY;// 
  dma_init_struct.memory_base_addr = (uint32_t)tx_buf; //
  dma_init_struct.memory_data_width = DMA_MEMORY_DATA_WIDTH_BYTE;
  dma_init_struct.memory_inc_enable = TRUE;
  dma_init_struct.peripheral_base_addr = (uint32_t)&USART1->dt; //从外设发给tx_buf接收
  dma_init_struct.peripheral_data_width = DMA_PERIPHERAL_DATA_WIDTH_BYTE;
  dma_init_struct.peripheral_inc_enable = FALSE;
  dma_init_struct.priority = DMA_PRIORITY_MEDIUM; 
  dma_init_struct.loop_mode_enable = FALSE;
  dma_init(DMA1_CHANNEL5, &dma_init_struct); //

  // 4.开启中断 并初始化NVIC
  //使能中断并配置

//   dma_interrupt_enable(DMA1_CHANNEL4,DMA_FDT_INT,TRUE); //全数据发送完产生中断，就是tx_bu发送结束
//   //所以这个tx_buf的打印优先于中断服务函数
//   dma_interrupt_enable(DMA1_CHANNEL5,DMA_FDT_INT,TRUE);

// // 中断函数的配置
//   nvic_priority_group_config(NVIC_PRIORITY_GROUP_4);
//   nvic_irq_enable(DMA1_Channel4_IRQn, 3, 1); //使能中断，如果是EXINT还有 抢占 响应（不打断） 数字小先
//   nvic_irq_enable(DMA1_Channel5_IRQn, 3, 0); //使能中断，如果是EXINT还有


  //5. dma通道使能 先关4 执行dma发送中断
  // 也就是先让tx_buf接收我们想发送发的数据
  // dma_channel_enable(DMA1_CHANNEL4, FALSE); /* usart2 rx begin dma receiving */
  dma_channel_enable(DMA1_CHANNEL5, TRUE); /* usart3 rx begin dma receiving */
}

// dma 快递柜
// tb mcu
// cus usart

// 内存到外设 送快递
// 外设到内存 

// 完整传输触发中断

// DMA的三种传输方式
// 外设→存储器（例：从串口RDR寄存器写入某数据buf） rx
// 数据从rx输入，接收数据移入rdr（接收数据缓存器）

// 存储器→外设（例：从某数据buf写入串口TDR寄存器）
//USART 具有一个发送数据缓冲器（TDR）和一个发送移位寄存器，
// 软件写入的值先进TDR中，然后进移位，最后从tx发出

// 存储器→存储器（例：复制某特别大的数据buf）
//https://blog.csdn.net/wxc971231/article/details/83044189